/*
 *  Copyright (C) 2010-2013 JPEXS
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.jpexs.helpers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 *
 * @author JPEXS
 */
public abstract class CancellableWorker<T> implements RunnableFuture<T> {
    
    private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
    private static List<CancellableWorker> workers = Collections.synchronizedList(new ArrayList<CancellableWorker>());
    private final FutureTask<T> future;
    
    public CancellableWorker() {
        super();
        Callable<T> callable =
                new Callable<T>() {
                    @Override
                    public T call() throws Exception {
                        return doInBackground();
                    }
                };

        future = new FutureTask<T>(callable) {
                       @Override
                       protected void done() {
                           workerDone();
                       }
                   };
    }

    protected abstract T doInBackground() throws Exception;

    @Override
    public final void run() {
        workers.add(this);
        future.run();
    }

    protected void done() {
    }
    
    public final void execute() {
        THREAD_POOL.execute(this);
    }

    @Override
    public final boolean cancel(boolean mayInterruptIfRunning) {
        return future.cancel(mayInterruptIfRunning);
    }

    @Override
    public final boolean isCancelled() {
        return future.isCancelled();
    }

    @Override
    public final boolean isDone() {
        return future.isDone();
    }

    @Override
    public final T get() throws InterruptedException, ExecutionException {
        return future.get();
    }

    @Override
    public final T get(long timeout, TimeUnit unit) throws InterruptedException,
            ExecutionException, TimeoutException {
        return future.get(timeout, unit);
    }

    private void workerDone() {
        workers.remove(this);
        done();
    }

    public static <T> T call(final Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
        CancellableWorker<T> worker = new CancellableWorker<T>() {

            @Override
            protected T doInBackground() throws Exception {
                return c.call();
            }
        };
        try {
            worker.execute();
            return worker.get(timeout, timeUnit);
        } finally {
            worker.cancel(true);
        }
    }
    
    public static void cancelBackgroundThreads() {
        List<CancellableWorker> oldWorkers = workers;
        workers = new ArrayList<>();
        for (CancellableWorker worker : oldWorkers) {
            worker.cancel(true);
        }
    }
}
